package httpapi

import (
	"log"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"syscall"
	"time"

	"gitee.com/daledi/public/daemon"
	"gitee.com/daledi/public/httpapi/apipprof"
	"gitee.com/daledi/public/httpapi/fiberprometheus"
	"gitee.com/daledi/public/httpapi/middleware"
	"gitee.com/daledi/public/options"
	"github.com/gofiber/fiber"
	"github.com/gofiber/fiber/middleware/logger"
)

const (
	ContextKey = "user"
	LocalIP    = "127.0.0.1"
)

var (
	JobChannel chan interface{}
)

type ApiServer struct {
	Name  string
	App   *fiber.App
	Api   fiber.Router
	Files []string
}

func New(name, conf string, front bool) *ApiServer {
	var err error
	if conf != "no" {
		if options.GolbalConf, err = options.ReadConfigP(conf); err != nil {
			log.Fatalf("Cant read %s: %v\n", conf, err)
		}
	} else {
		options.GolbalConf = options.GOPTP()
	}
	gConf := options.GolbalConf
	// dmfiles := []string{}
	dmfiles := []string{"", "/dev/stdout", "/dev/stderr"}
	if !fiber.IsChild() && !front {
		daemon.Daemon(gConf.PidFile, dmfiles, "")
	}
	maxprocs := os.Getenv("GOMAXPROCS")
	if maxprocs != "" {
		maxProc, _ := strconv.Atoi(maxprocs)
		if maxProc > 0 {
			runtime.GOMAXPROCS(maxProc)
		}
	}
	logfd, err := os.OpenFile(gConf.LogDir+"/access.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatalf("open log file failed: %v\n", err)
	}
	app := fiber.New(fiber.Config{
		Prefork:               !front,
		CaseSensitive:         true,
		StrictRouting:         true,
		ServerHeader:          "httpapi",
		ErrorHandler:          middleware.ErrorHandler,
		DisableKeepalive:      true,
		DisableStartupMessage: true,
		IdleTimeout:           time.Second * 5,
		ReadTimeout:           time.Second * 20,
		WriteTimeout:          time.Second * 20,
		AppName:               name,
	})
	app.Use(middleware.PanicHandler)
	app.Use(logger.New(logger.Config{
		Format: "${ips} ${time} ${method} ${url} ${status} ${bytesReceived} ${bytesSent} ${latency} ${error}\n",
		Output: logfd,
	}))
	api := app.Group(gConf.Prefix)
	apipprof.AddRoutes(api)
	p := fiberprometheus.NewMiddleware("fiber", "http", "/metrics")
	p.Register(api)

	return &ApiServer{
		Name:  name,
		App:   app,
		Api:   api,
		Files: dmfiles,
	}
}

func (as *ApiServer) Run(job func(gConf *options.GlobalOPT)) {
	gConf := options.GolbalConf
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1, syscall.SIGUSR2)
	readly := make(chan bool)
	readly1 := make(chan bool)
	if !fiber.IsChild() {
		// fmt.Printf("main pid: %d\n", os.Getpid())
		// httpapi
		if job != nil {
			go job(gConf)
		}
		go func() {
			if err := as.App.Listen(gConf.Bind + ":" + gConf.Port); err != nil {
				log.Panic(err)
			}
		}()

		switch <-ch {
		case syscall.SIGINT, syscall.SIGTERM:
			signal.Stop(ch)
			as.App.Shutdown()
			gConf.ErrLog.Rotate()
		// case syscall.SIGUSR1:
		// 	if options.GolbalConf, err = options.ReadConfigP(*conf); err != nil {
		// 		log.Fatalf("Cant read %s: %v\n", *conf, err)
		// 	}
		case syscall.SIGUSR2:
			//DAEMONKEY2: 在usr2下重启时，新的主进程不退出，而是返回老的主进程。
			// readly1：确保在新进程完成后，再退出app。
			// readly：确保在shutdown执行完成后，再退出老的主进程。
			os.Unsetenv(daemon.DAEMONKEY)
			os.Setenv(daemon.DAEMONKEY2, "1")
			gConf.ErrLog.Rotate()
			go func() {
				<-readly1
				as.App.Shutdown()
				close(readly)
			}()
			daemon.Daemon(gConf.PidFile, as.Files, "")
			// 确保新的master和child启动完成
			time.Sleep(time.Second * 5)
			close(readly1)
			<-readly
		}
	} else {
		// fmt.Printf("child pid: %d\n", os.Getpid())
		as.App.Listen(gConf.Bind + ":" + gConf.Port)
	}
}
